package io.polly.thread;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.NamedThreadFactory;
import io.polly.common.constant.DynamicExecutorConstant;
import io.polly.common.entity.NotifyItem;
import io.polly.common.util.DynamicExecutorUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.util.List;
import java.util.concurrent.*;

/**
 * @author ldz
 * @version V1.0
 * @title: DynamicExecutorBuilder
 * @description: 建造者
 * @date: 2022/3/21 11:21
 */

public class DynamicExecutorBuilder {

    private String threadPoolName = DynamicExecutorConstant.DYNAMIC_EXECUTOR_NAME;

    private int corePoolSize = 1;

    private int maximumPoolSize = DynamicExecutorConstant.AVAILABLE_PROCESSORS;

    private long keepAliveTime = 30;

    private TimeUnit timeUnit = TimeUnit.SECONDS;

    private BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(1024);

    private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();

    /** Default inner thread factory.*/
    private ThreadFactory threadFactory = new NamedThreadFactory(DynamicExecutorConstant.DYNAMIC_EXECUTOR_NAME,false);

    /** If allow core thread timeout.*/
    private boolean allowCoreThreadTimeOut = false;

    /** Build Dynamic or common threadPoolExecutor.*/
    private boolean dynamic = true;

    private List<NotifyItem> notifyItems = NotifyItem.getDefaultNotifyItems();

    private DynamicExecutorBuilder() {}

    public static DynamicExecutorBuilder newBuilder(){ return new DynamicExecutorBuilder();}

    /** ---------------------- */

    public DynamicExecutorBuilder threadPoolName(String executorName){
        this.threadPoolName = executorName;
        return this;
    }

    public DynamicExecutorBuilder corePoolSize(int corePoolSize) {
        if (corePoolSize >= 0) {
            this.corePoolSize = corePoolSize;
        }
        return this;
    }

    public DynamicExecutorBuilder maximumPoolSize(int maximumPoolSize) {
        if (maximumPoolSize > 0) {
            this.maximumPoolSize = maximumPoolSize;
        }
        return this;
    }

    public DynamicExecutorBuilder keepAliveTime(long keepAliveTime) {
        if (keepAliveTime > 0) {
            this.keepAliveTime = keepAliveTime;
        }
        return this;
    }

    public DynamicExecutorBuilder timeUnit(TimeUnit timeUnit) {
        if (timeUnit != null) {
            this.timeUnit = timeUnit;
        }
        return this;
    }

    public DynamicExecutorBuilder workQueue(String queueName, Integer capacity, Boolean fair) {
        if (StringUtils.isNotBlank(queueName)) {
            workQueue = DynamicExecutorUtil.buildBlockingQueue(queueName,
                    capacity != null ? capacity : 1024, fair != null && fair);
        }
        return this;
    }



    public DynamicExecutorBuilder rejectedExecutionHandler(String rejectedName) {
        if (StringUtils.isNotBlank(rejectedName)) {
            rejectedExecutionHandler = DynamicExecutorUtil.buildRejectedHandler(rejectedName);
        }
        return this;
    }

    public DynamicExecutorBuilder threadFactory(String prefix) {
        if (StringUtils.isNotBlank(prefix)) {
            threadFactory = new NamedThreadFactory(prefix,false);
        }
        return this;
    }

    public DynamicExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
        return this;
    }

    public DynamicExecutorBuilder dynamic(boolean dynamic) {
        this.dynamic = dynamic;
        return this;
    }

    public DynamicExecutorBuilder notifyItems(List<NotifyItem> notifyItemList) {
        if (CollUtil.isNotEmpty(notifyItemList)) {
            notifyItems = notifyItemList;
        }
        return this;
    }

    public ThreadPoolExecutor build() {
        if (dynamic) {
            return buildDynamicExecutor(this);
        } else {
            return buildCommonExecutor(this);
        }
    }

    public DynamicExecutor buildDynamicExecutor() {
        return buildDynamicExecutor(this);
    }

    public ThreadPoolExecutor buildCommonExecutor() {
        return buildCommonExecutor(this);
    }

    private DynamicExecutor buildDynamicExecutor(DynamicExecutorBuilder builder) {
        Assert.notNull(builder.threadPoolName, "The thread pool name must not be null.");
        DynamicExecutor dynamicExecutor = new DynamicExecutor(
                builder.corePoolSize,
                builder.maximumPoolSize,
                builder.keepAliveTime,
                builder.timeUnit,
                builder.workQueue,
                builder.threadFactory,
                builder.rejectedExecutionHandler
        );
        dynamicExecutor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut);
        dynamicExecutor.setThreadPoolName(builder.threadPoolName);
        dynamicExecutor.setNotifyItems(notifyItems);
        return dynamicExecutor;
    }

    private ThreadPoolExecutor buildCommonExecutor(DynamicExecutorBuilder builder) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                builder.corePoolSize,
                builder.maximumPoolSize,
                builder.keepAliveTime,
                builder.timeUnit,
                builder.workQueue,
                builder.threadFactory,
                builder.rejectedExecutionHandler
        );
        executor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut);
        return executor;
    }

}
